package com.tarena.charging.order.websocket;

import java.io.IOError;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Slf4j
@Component
//提供一个终端的注解 当前项目启动后可以利用这个类bean对象实现和前端交互
@ServerEndpoint("/ws/server/{userId}")
public class WebSocketServer {
    /**
     * webSocketServer服务端 为了不混淆消息数据推送的错乱结果
     * 保存和前端通信session对象 但是直接使用session管理连接
     * 比较麻烦 得记录一个session和业务数据的映射关系
     * 当前这个业务 我想把消息推给某一个正在充电的用户
     * 所以可以利用userId的值 保存和session映射关系
     * userId(3)--session1
     * userId(4)--session2
     */
    /**
     * 如何保存userId和session的关系
     * userId是一个整数
     * session就是一个session对象
     * 创建一个注册器 把每个连接当前服务端的用户userId和session做对应
     */
    private static ConcurrentHashMap<Integer, Session> session_register=
        new ConcurrentHashMap<>();

    /**
     * 用户通过ServerEndpoint连接当前服务端
     * 连接可以通过以下方法建立,同时初始化当前会话和userId绑定关系
     * 开始建立连接的时候 会调用@OnOpen方法
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") Integer userId){
        log.info("有用户开始建立连接:{}",userId);
        //将userId 和session对象保存在注册器中 后续只要我知道userId是几,就能拿到对应session进行消息推送
        session_register.put(userId,session);
        log.info("用户:{},建立连接成功:{}",userId,session.getId());
    }
    //连接session关闭的时候会调用OnClose方法 清理工作 移除userId-session关系
    @OnClose
    public void onClose(Session session,@PathParam("userId") Integer userId){
        log.info("有用户开始关闭连接:{}",userId);
        //将userId 和session对象保存在注册器中 后续只要我知道userId是几,就能拿到对应session进行消息推送
        session_register.remove(userId);
        log.info("用户:{},关闭连接成功:{}",userId,session.getId());
    }
    //如果我们建立连接出现了问题 异常
    @OnError
    public void onError(Session session,Throwable e){
        log.error("连接出现异常:{}",e.getMessage(),e);
    }

    //每次通信 都会调用这个方法
    @OnMessage
    public void onMessage(String message,Session session){
        log.info("开始通信消息");
        log.info("会话:{},发送消息:{}",session,message);
    }

    //后端 想要通过 业务的数据 推送消息给建立连接的用户客户端
    //需要使用 session对象 当前WebSocketServer可以注入到也中使用,在这个类中 创建一个推送消息的方法
    public Boolean pushMessage(Integer userId,String message){
        //1.在业务中能够获取userId,映射的一个session可以从注册中拿到
        Session session = session_register.get(userId);
        if (session!=null){
            //2.说明这个是注册中一个合法的连接session
            try{
                log.error("当前用户:{},使用session:{},推送消息:{}",userId,session,message);
                session.getBasicRemote().sendText(message);
                return true;
            }catch (IOException e){
                log.error("推送消息失败",e);
            }
        }else{
            //3.说明这个user已经下线了
            log.error("当前用户:{},已经下线",userId);
        }
        return false;
    }
}
